#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

/*
 * setpgid(pid,pgid) 的作用
 * 1)创建新进程组，pgid = pid 
 * 2)将进程移动到同一会话的其他进程组(记住是同一会话)， pgid = XXX(XXX需要是会话中的某一个进程组) 
 *
 * */

void test1()
{
	pid_t pid;
	if ((pid = fork()) < 0)
	{
		exit(1);
	} else if (pid == 0)
	{
		sleep(2);
		printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
		exit(0);
	}

	printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
	/* 设置子进程pid,的组ID为：pid, 则pid进程将成为新的进程组pid的组长 */
	if (setpgid(pid, pid) < 0)
	{
		perror("setpgid error:");
	}
}

void test2()
{
	pid_t pid;
	if ((pid = fork()) < 0)
	{
		exit(1);
	} else if (pid == 0)
	{
		sleep(2);
		printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
		exit(0);
	}

	printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
	/* 设置进程自身的组ID */
	if (setpgid(getpid(), getpid()) < 0)
	{
		perror("setpgid error:");
		exit(1);
	}
	printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
	/* 当进程改变自身的组ID后，还是可以设置子进程的组ID 
	 * 只要是被设置的进程是该进程的子进程即可
	 * */
	/* setpgid(XXX,getpid()) 等价于 setpgid(XXX,0) 
	 * setpgid(getpid(),XXX) 等价于 setpgid(0,XXX)
	 * */
	if ((setpgid(pid,pid)) < 0)
	{
		perror("setpgid error:");
		exit(2);
	}
}
 
void test3()
{
	pid_t pid;
	if ((pid = fork()) < 0)
	{
		exit(1);
	} else if (pid == 0)
	{
		/* execl之前先打印子进程的ID与组ID */
		printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
		/* "./sleep_output_getpid_getpgrp.out 暂停3S后打印进程的ID与组ID " */
		execl("./sleep_output_getpid_getpgrp.out","sleep_output_getpid_getpgrp.out",NULL);
		exit(0);
	}
	/* 父进程先暂停 */
	sleep(2);
	printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
	/* 设置子进程pid,的组ID为：pid */
	if (setpgid(pid, pid) < 0)
	{
		/*
			setpgid error:: Permission denied
			当子进程使用exec后，父进程无法再设置子进程的组ID
		*/
		perror("setpgid error:");
	}
}

void test4()
{
	pid_t pid;
	pid = fork();
	/* 这个语句子、父进程都会执行，这样保证父、子进程在进一步操作前，子进程都进入了该进程组。无论是谁先执行 */
	switch(pid) {
		case -1:
			perror("fork error");
			exit(1);
		case 0:
			if (setpgid(pid,pid) == -1)
			{
				perror("child setpgid error");
				exit(1);
			}
			printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
			exit(0);
		default:
			if (setpgid(pid,pid) == -1 && errno != EACCES)
			{
				perror("father setpgid error");
				exit(1);
			}
			printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
	}
}
/*
 * 先在shell中运行"nostop_getpid_getpgrp.out &",并获得该进程ID：XXX
 * 执行"./process_group_2.out"  然后输入XXX
 * */
void test5()
{
	pid_t pid;
	pid_t pgid;
	pid = fork();
	switch(pid) {
		case -1:
			perror("fork error");
			exit(1);
		case 0:
			printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
			sleep(6);
			printf("child: pid:%u pgid:%u\n",getpid(),getpgrp());
			exit(0);
		default:
			printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
			scanf("%u",&pgid);
			/* success,进程组组长可以移动到同一会话中的另一个进程组 */
			setpgid(0,pgid);
			printf("father: pid:%u pgid:%u\n",getpid(),getpgrp());
			if (setsid() < 0)
			{
				/* 当进程为进程组组长(即使该进程组只有他一个进程)，也不能创建一个新的会话
				 * 即使该进程不是进程组组长,但仍存在gid为该进程ID的进程组时，也不能创建一个新的会话
				 * 假设某个进程的pid为A，则该进程可以创建新会话的条件是：目前没有任何一个进程组的ID为A
				 * */
				perror("setsid error");
			}
	}
}

void test6()
{
	pid_t pgid;
	printf(" pid:%u pgid:%u sid:%u\n",getpid(),getpgrp(),getsid(0));
	scanf("%u",&pgid);
	setpgid(0, pgid);
	printf(" pid:%u pgid:%u sid:%u\n",getpid(),getpgrp(),getsid(0));
	if (setsid() < 0)
	{
		perror("setsid error");
	} else 
	{
		printf("setsid success\n");
		/* 创建新会话后，pgid又变成pid */
		printf(" pid:%u pgid:%u sid:%u\n",getpid(),getpgrp(),getsid(0));
		/*
		if (open("/dev/pts/1",O_RDWR) < 0)
		{
			perror("open error");
		}
		*/
	}
}

int main()
{
	test6();
	return 0;
}
